package org.tamal.persistence;

import java.util.Set;

import javax.persistence.Cacheable;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;

import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.DynamicUpdate;

/**
 * The Employee class.
 * @author Tamal Kanti Nath
 */
@NamedQueries({
    @NamedQuery(name = "findCeoLazy", query = "select e from Employee e where e.manager is null"),
    @NamedQuery(name = "findCeoJoin", query = "select e from Employee e join fetch e.reportees where e.manager is null")
})
@Entity
@Cacheable
@DynamicUpdate
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class Employee extends Person {

    private Branch branch;
    private Employee manager;
    private Set<Employee> reportees;

    /**
     * Default constructor.
     */
    public Employee() {
        super();
    }

    /**
     * Constructor with two parameters.
     * @param id the ID to set
     * @param name the name to set
     */
    public Employee(Long id, String name) {
        setId(id);
        setName(name);
    }

    /**
     * Returns the branch.
     * @return the branch
     */
    @ManyToOne(optional = false, cascade = CascadeType.ALL)
    public Branch getBranch() {
        return branch;
    }

    /**
     * Sets the branch.
     * @param branch the branch to set
     */
    public void setBranch(Branch branch) {
        this.branch = branch;
    }

    /**
     * Returns the manager.
     * @return the manager
     */
    @ManyToOne(optional = true, cascade = CascadeType.ALL)
    public Employee getManager() {
        return manager;
    }

    /**
     * Sets the manager.
     * @param manager the manager to set
     */
    public void setManager(Employee manager) {
        this.manager = manager;
    }

    /**
     * Returns the reportees.
     * @return the reportees
     */
    @OneToMany(mappedBy = "manager", cascade = CascadeType.ALL)
    public Set<Employee> getReportees() {
        return reportees;
    }

    /**
     * Sets the reportees.
     * @param reportees the reportees to set
     */
    public void setReportees(Set<Employee> reportees) {
        this.reportees = reportees;
    }

    /**
     * Indicates whether the specified object equals to this object.<br/>
     * For Employee entity, the personal data (name, birth and sex), manager and
     * branch are considered as business key as they are less likely to change
     * and can be used to identify a record uniquely. Two employees are
     * considered equal if their personal data (name, birth and sex), manager
     * and branch are equal.
     *
     * @param obj the reference object with which to compare
     * @return if this Employee is the same as the obj argument; false otherwise
     */
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (!(obj instanceof Employee)) {
            return false;
        }
        Employee e = (Employee) obj;
        if (manager == null) {
            if (super.equals(e) && branch.equals(e.branch)
                    && e.manager == null) {
                return true;
            }
        } else {
            if (super.equals(e) && branch.equals(e.branch)
                    && manager.equals(e.manager)) {
                return true;
            }
        }
        return false;
    }

    /**
     * The hash code is generated from superclass (person) branch and manager.
     * @return hash code value for employee
     */
    @Override
    public int hashCode() {
        if (manager == null) {
            return super.hashCode() ^ branch.hashCode();
        }
        return super.hashCode() ^ branch.hashCode() ^ manager.hashCode();
    }

}
